package com.example.luyao.myapplication;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class QuadBezierView extends View {
    private final static String TAG = Constants.LOG_PREFIX + QuadBezierView.class.getCanonicalName();
    private List<Point> list1 = new ArrayList<>();
    private List<Point> list2 = new ArrayList<>();
    private List<Point> list3 = new ArrayList<>();
    private List<Point> list4 = new ArrayList<>();
    private List<Point> list5 = new ArrayList<>();
    private List<Point> list6 = new ArrayList<>();

    private static int x_max = 20;
    private static int y_max = 260;
    private static int y_interval = 20;

    // 默认边距
    private int margin = 20;
    // 距离左边偏移量
    private int marginX = 30;
    // 原点坐标
    private int origin_x;
    private int origin_y;
    // X,Y轴的单位长度
    private int xScale;
    private int yScale;
    // 画笔
    private Paint paintAxes;
    private Paint paintCoordinate;
    private Paint paintTable;
    private Paint paintCurve;
    private Paint paintRectF;
    private Paint paintValue;

    private List<String> xLabel = new ArrayList<>();
    private List<String> yLabel = new ArrayList<>();
    private List<Integer> colorList;
    private List<List<Point>> dataList;
    private static int lastPointIndex;
    private static long lastPointTime;

    public QuadBezierView(Context context) {
        super(context);
    }

    public QuadBezierView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void changeTemperatureData(int i){
        dataList.clear();
        if(i == 0) dataList.add(list1);
        else if(i == 1) dataList.add(list2);
        else if(i == 2) dataList.add(list3);
        else if(i == 3) dataList.add(list4);
        else if(i == 4) dataList.add(list5);
        else if(i == 5) {
            dataList.add(list6);
            Log.e(TAG, "changeTemperatureData");
        }
    }

    public QuadBezierView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        for(int i = 0; i <= x_max; i+=2) xLabel.add(Integer.toString(i));
        Log.e(TAG, "QuadBezierView " + xLabel.size());
        for(int i = 0; i <= y_max; i+=y_interval) yLabel.add(Integer.toString(i));
        Log.e(TAG, "QuadBezierView " + yLabel.size());

        init_time_temperature();
        dataList = new ArrayList<>();
        dataList.add(list1);

        colorList = new ArrayList<>();
        colorList.add(R.color.color14);
        colorList.add(R.color.color13);
        colorList.add(R.color.color25);

        origin_x = margin + marginX;

        paintAxes = new Paint();
        paintAxes.setStyle(Paint.Style.STROKE);
        paintAxes.setAntiAlias(true);
        paintAxes.setDither(true);
        paintAxes.setColor(ContextCompat.getColor(getContext(), R.color.color14));
        paintAxes.setStrokeWidth(4);

        paintCoordinate = new Paint();
        paintCoordinate.setStyle(Paint.Style.STROKE);
        paintCoordinate.setDither(true);
        paintCoordinate.setAntiAlias(true);
        paintCoordinate.setColor(ContextCompat.getColor(getContext(), R.color.color14));
        paintCoordinate.setTextSize(18);

        paintTable = new Paint();
        paintTable.setStyle(Paint.Style.STROKE);
        paintTable.setAntiAlias(true);
        paintTable.setDither(true);
        paintTable.setColor(ContextCompat.getColor(getContext(), R.color.color4));
        paintTable.setStrokeWidth(2);
        paintTable.setStyle(Paint.Style.FILL);

        paintCurve = new Paint();
        paintCurve.setStyle(Paint.Style.STROKE);
        paintCurve.setDither(true);
        paintCurve.setAntiAlias(true);
        paintCurve.setStrokeWidth(3);
        //PathEffect pathEffect = new CornerPathEffect(25);
        //paintCurve.setPathEffect(pathEffect);

        paintRectF = new Paint();
        paintRectF.setStyle(Paint.Style.FILL);
        paintRectF.setDither(true);
        paintRectF.setAntiAlias(true);
        paintRectF.setStrokeWidth(3);

        paintValue = new Paint();
        paintValue.setStyle(Paint.Style.STROKE);
        paintValue.setAntiAlias(true);
        paintValue.setDither(true);
        paintValue.setColor(ContextCompat.getColor(getContext(), R.color.color1));
        paintValue.setTextAlign(Paint.Align.CENTER);
        paintValue.setTextSize(15);

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        Log.e(TAG, "onSizeChanged w " + w + " h " + h + " oldw " + oldw + " oldh " + oldh );
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        origin_y = this.getHeight() - margin;
        xScale = (this.getWidth() - 2 * margin - marginX) / (xLabel.size() - 1);
        yScale = (this.getHeight() - 2 * margin) / (yLabel.size() - 1);
        //drawTable(canvas, paintTable);
        drawBackgroudColor(canvas, paintTable);
        drawAxesLine(canvas, paintAxes);
        drawCoordinate(canvas, paintCoordinate);

        for (int i = 0; i < 1; i++) {
            drawCurve(canvas, paintCurve, dataList.get(i), colorList.get(i));
            drawValue(canvas, paintRectF, dataList.get(i), colorList.get(i));
        }
    }

    private void update_data(int i, Date t, float x, float y, List<Point> points){
        Point p = points.get(i);
        float x_p = toX(p.x);
        float y_p = toY(p.y);
        lastPointIndex = i;
        lastPointTime = t.getTime();
        Log.e(TAG, "update_point near point " + i + " x_p " + x_p + " " + y_p);
        float x_target = (x - origin_x) * 2.0f / xScale;
        Point before = null;
        Point next = null;
        if(i - 1 >= 0) {
            before = points.get(i - 1);
        }
        if(i + 1 < points.size()) {
            next = points.get(i + 1);
        }
        float min_x_delta = 0.8f;
        if(before != null){
            if(x_target < before.x + min_x_delta) x_target = before.x + min_x_delta;
        } else {
            if(x_target < 0) x_target = 0.001f;
        }

        if(next != null){
            if(x_target > next.x - min_x_delta) x_target = next.x - min_x_delta;
        } else {
            if(x_target > x_max) x_target = x_max;
        }

        float y_target = (origin_y - y) * y_interval / yScale;
        if(y_target > y_max) y_target = y_max;
        if(y_target < 0) y_target = 0;

        p.x = x_target;
        p.y = y_target;
        Log.e(TAG, "update_point update point x_target " + x_target + " " + y_target);
    }

    private void update_point(float x, float y){
        Date t = new java.util.Date();
        //Log.e(TAG, "update_point " + x + " " + y);
        List<Point> points = dataList.get(0);
        if(t.getTime() - lastPointTime < 50){
            update_data(lastPointIndex, t, x, y, points);
            return;
        }
        for(int i = 0; i < points.size(); i++){
            Point p = points.get(i);
            float x_p = toX(p.x);
            float y_p = toY(p.y);
            //Log.e(TAG, "update_point x_p " + x_p + " " + y_p);
            if( Math.sqrt(Math.pow(x_p - x, 2.0f) + Math.pow(y_p - y, 2.0f)) < 50){
                update_data(i, t, x, y, points);
                return;
            }
        }
    }

    public void addPoint(float x, float y){
        if(dataList == null || dataList.size() == 0) return;
        List<Point> points = dataList.get(0);
        for(int i = 0; i < points.size(); i++) {
            Point p = points.get(i);
            if(p.x > x){
                points.add(i, new Point(x, y));
                invalidate();
                return;
            }
        }
        points.add(new Point(x, y));
        invalidate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(TAG, "onTouchEvent " + event.getAction());

        float x, y;
        switch (event.getAction()){
        case MotionEvent.ACTION_MOVE:
            x = event.getX();
            y = event.getY();
            update_point(x, y);
            //表示当前View失效，如故View是可见的，则onDraw方法会被调用
            //注意：该方法需要在UI线程中执行
            invalidate();
            break;
        case MotionEvent.ACTION_UP: //手指抬起时
            x = event.getX();
            y = event.getY();
            invalidate();
            break;
        }
        return true;
    }

    private void drawCurve(Canvas canvas, Paint paint, List<Point> data, int color) {
        paint.setColor(ContextCompat.getColor(getContext(), color));
        Path path = new Path();
        for (int i = 0; i < data.size(); i++) {
            Point p = data.get(i);
            if (i == 0) {
                //Log.e(TAG, "drawCurve " + i + " " + p + toX(p.x) + " " + toY(p.y));
                path.moveTo(toX(p.x), toY(p.y));
            } else {
                //Log.e(TAG, "drawCurve " + i + " " + p + toX(p.x) + " " + toY(p.y));
                path.lineTo(toX(p.x), toY(p.y));
            }
        }
        canvas.drawPath(path, paint);
    }

    /*** 绘制数值 */
    private void drawValue(Canvas canvas, Paint paint, List<Point> data, int color) {
        paint.setColor(ContextCompat.getColor(getContext(), color));
        for (int i = 1; i < data.size(); i++) {
            Point p = data.get(i);
            float x = toX(p.x);
            float y = toY(p.y);
            String value = Integer.toString((int)p.y);
            Point p_before = data.get(i - 1);
            float y_next = toY(p_before.y);
            RectF rect;
            if (y > y_next) {
                rect = new RectF(x - 20, y - 15, x + 20, y + 5);
                canvas.drawRoundRect(rect, 5, 5, paint);
                canvas.drawText(value, x, y, paintValue);
            } else if (y < y_next) {
                rect = new RectF(x - 20, y - 5, x + 20, y + 15);
                canvas.drawRoundRect(rect, 5, 5, paint);
                canvas.drawText(value, x, y + 10, paintValue);
            } else {
                rect = new RectF(x - 20, y - 10, x + 20, y + 10);
                canvas.drawRoundRect(rect, 5, 5, paint);
                canvas.drawText(value, x, y + 5, paintValue);
            }
        }
    }

    private float toX(float num) {
        return origin_x + num / 2.0f * xScale;
    }

    /*** 数据按比例转坐标 */
    private float toY(float num) {
        return origin_y - num / y_interval * yScale;
    }

    private void drawAxesLine(Canvas canvas, Paint paint) {
        // X
        /*
          Log.e(TAG, "drawAxesLine " + "origin_x " + origin_x + " origin_y " + origin_y +
          " this.getWidth() " + this.getWidth() + " this.getHeight() " + this.getHeight() +
          " margin " + margin + " " + (this.getWidth() - margin / 6));
        */
        canvas.drawLine(origin_x, origin_y, this.getWidth() - margin / 6, origin_y, paint);
        canvas.drawLine(this.getWidth() - margin / 6, origin_y,
                        this.getWidth() - margin / 2, origin_y - margin / 3, paint);
        canvas.drawLine(this.getWidth() - margin / 6, origin_y,
                        this.getWidth() - margin / 2, origin_y + margin / 3, paint);

        // Y
        canvas.drawLine(origin_x, origin_y, origin_x, margin / 6, paint);
        canvas.drawLine(origin_x, margin / 6, origin_x - margin / 3, margin / 2, paint);
        canvas.drawLine(origin_x, margin / 6, origin_x + margin / 3, margin / 2, paint);
    }

    private void drawBackgroudColor(Canvas canvas, Paint paint) {
        Rect r = new Rect(origin_x, (int)toY(40), (int)toX(x_max), origin_y);
        paint.setARGB(255, 103, 210, 223);
        canvas.drawRect(r, paint);

        paint.setARGB(255, 223, 236, 107);
        r = new Rect(origin_x, (int)toY(150), (int)toX(x_max), (int)toY(40));
        canvas.drawRect(r, paint);
        
        paint.setARGB(255, 246, 235, 97);
        r = new Rect(origin_x, (int)toY(165), (int)toX(x_max), (int)toY(150));
        canvas.drawRect(r, paint);
        
        paint.setARGB(255, 248, 198, 166);
        r = new Rect(origin_x, (int)toY(195), (int)toX(x_max), (int)toY(165));
        canvas.drawRect(r, paint);
        
        paint.setARGB(255, 214, 154, 45);
        r = new Rect(origin_x, (int)toY(y_max), (int)toX(x_max), (int)toY(195));
        canvas.drawRect(r, paint);
    }

    private void drawTable(Canvas canvas, Paint paint) {
        Path path = new Path();
        // 横向线
        for (int i = 1; (origin_y - i * yScale) >= margin; i++) {
            int startX = origin_x;
            int startY = origin_y - i * yScale;
            int stopX = origin_x + (xLabel.size() - 1) * xScale;
            path.moveTo(startX, startY);
            path.lineTo(stopX, startY);
            canvas.drawPath(path, paint);
        }

        // 纵向线
        for (int i = 1; i * xScale <= (this.getWidth() - margin); i++) {
            int startX = origin_x + i * xScale;
            int startY = origin_y;
            int stopY = origin_y - (yLabel.size() - 1) * yScale;
            path.moveTo(startX, startY);
            path.lineTo(startX, stopY);
            canvas.drawPath(path, paint);
        }
    }

    /*** 绘制刻度*/
    private void drawCoordinate(Canvas canvas, Paint paint) {
        // X轴坐标
        for (int i = 0; i <= (xLabel.size() - 1); i++) {
            paint.setTextAlign(Paint.Align.CENTER);
            int startX = origin_x + i * xScale;
            canvas.drawText(xLabel.get(i), startX, this.getHeight() - margin / 6, paint);
        }

        // Y轴坐标
        for (int i = 0; i <= (yLabel.size() - 1); i++) {
            paint.setTextAlign(Paint.Align.LEFT);
            int startY = origin_y - i * yScale;
            int offsetX;
            switch (yLabel.get(i).length()) {
            case 1:
                offsetX = 28;
                break;

            case 2:
                offsetX = 20;
                break;

            case 3:
                offsetX = 12;
                break;

            case 4:
                offsetX = 5;
                break;

            default:
                offsetX = 0;
                break;
            }
            int offsetY;
            if (i == 0) {
                offsetY = 0;
            } else {
                offsetY = margin / 5;
            }
            // x默认是字符串的左边在屏幕的位置，y默认是字符串是字符串的baseline在屏幕上的位置
            canvas.drawText(yLabel.get(i), margin / 4 + offsetX, startY + offsetY, paint);
        }
    }

    public class Point {
        public float x;
        public float y;

        public Point(float x, float y) {
            this.x = x;
            this.y = y;
        }

        @NonNull
        @Override
        public String toString() {
            return "[Point x: " + x + " y: " + y + "] ";
        }
    }

    private void init_time_temperature(){
        Log.e(TAG, "init_time_temperature");
        list1.add(new Point(0f, 20f));
        list1.add(new Point(3f, 131f));
        list1.add(new Point(5f, 160f));
        list1.add(new Point(7.5f, 185f));
        list1.add(new Point(9f, 195f));
        list1.add(new Point(12f, 40f));

        list2.add(new Point(0f, 20f));
        list2.add(new Point(3f, 131f));
        list2.add(new Point(5f, 162f));
        list2.add(new Point(7.5f, 192f));
        list2.add(new Point(9.5f, 205));
        list2.add(new Point(13f, 40f));

        list3.add(new Point(0f, 20f));
        list3.add(new Point(3f, 131f));
        list3.add(new Point(5f, 162f));
        list3.add(new Point(8f, 192f));
        list3.add(new Point(12f, 210f));
        list3.add(new Point(15.5f, 40f));

        list4.add(new Point(0f, 20f));
        list4.add(new Point(3f, 131f));
        list4.add(new Point(5f, 160f));
        list4.add(new Point(7.5f, 197f));
        list4.add(new Point(12.5f, 220f));
        list4.add(new Point(16.5f, 40f));

        list5.add(new Point(0f, 20f));
        list5.add(new Point(3f, 131f));
        list5.add(new Point(5f, 160f));
        list5.add(new Point(7.5f, 197f));
        list5.add(new Point(13f, 220f));
        list5.add(new Point(17f, 40f));

        list6.add(new Point(0f, 20f));
        list6.add(new Point(3f, 131f));
        list6.add(new Point(5f, 160f));
        list6.add(new Point(7.5f, 197f));
        list6.add(new Point(13f, 220f));
        list6.add(new Point(18f, 40f));
    }
}
